package com.example.yupao.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.yupao.constant.UserConstant;
import com.example.yupao.enums.StatusEnum;
import com.example.yupao.enums.TeamStatusEnum;
import com.example.yupao.exception.BusinessException;
import com.example.yupao.mapper.TeamMapper;
import com.example.yupao.model.domain.Team;
import com.example.yupao.model.domain.User;
import com.example.yupao.model.domain.UserTeam;
import com.example.yupao.model.request.TeamAddRequest;
import com.example.yupao.model.request.TeamJoinRequest;
import com.example.yupao.model.request.TeamQuitRequest;
import com.example.yupao.model.request.TeamSearchRequest;
import com.example.yupao.model.vo.TeamVO;
import com.example.yupao.model.vo.UserVO;
import com.example.yupao.service.TeamService;

import com.example.yupao.service.UserService;
import com.example.yupao.service.UserTeamService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.example.yupao.constant.RedisConstant.TEAM_ADD_LOCK;

/**
 * @author 86138
 * @description 针对表【team】的数据库操作Service实现
 * @createDate 2024-09-18 22:47:52
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
        implements TeamService {

    @Resource
    private UserService userService;

    @Resource
    private UserTeamService userTeamService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean addTeam(TeamAddRequest teamAddRequest, HttpServletRequest request) {

        //1. 请求参数是否为空
        if (teamAddRequest == null) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "请求参数错误");
        }
        //2. 用户是否登录,未登录不允许创建队伍
        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        //3.校验信息
        hasTeamSaveRequest(teamAddRequest);

        //redis 加锁解决 并发创建问题    在同一时间段内,连续创建多个队伍(1s内用户一直点新增按钮)
//        Boolean lock = redisTemplate.opsForValue().setIfPresent(TEAM_ADD_LOCK + userId, userId, 10, TimeUnit.SECONDS);
        Boolean lock = redisTemplate.opsForValue().setIfAbsent(TEAM_ADD_LOCK + userId, userId, 10, TimeUnit.SECONDS);
        if (lock == null || lock == false) {
            throw new BusinessException(StatusEnum.NULL_ERROR, "请不要重复提交");
        } else {
            //执行业务操作
            try {
                //4. 插入队伍信息到 team表
                Team team = new Team();
                String teamName = teamAddRequest.getName();
                team.setName(teamName);
                String description = teamAddRequest.getDescription();
                team.setDescription(description);
                Integer teamStatus = teamAddRequest.getStatus();
                team.setStatus(TeamStatusEnum.getEnumByValue(teamStatus));
                String password = teamAddRequest.getPassword();
                if (password != null) {
                    team.setPassword(password);
                }
                team.setCreateUserId(userId);
                Integer maxNum = teamAddRequest.getMaxNum();
                team.setMaxNum(maxNum);
                Date expireTime = teamAddRequest.getExpireTime();
                team.setExpireTime(expireTime);
                //插入 team
                boolean result = this.save(team);
                if (!result) {
                    throw new BusinessException(StatusEnum.SYSTEM_ERROR, "系统出错,请稍后重试~");
                }

                //5. 插入 创建者信息到 user_team表
                Long teamId = team.getId();
                UserTeam userTeam = new UserTeam();
                userTeam.setUserId(userId);
                userTeam.setTeamId(teamId);

                boolean userTeamResult = userTeamService.save(userTeam);
                if (!userTeamResult) {
                    throw new BusinessException(StatusEnum.SYSTEM_ERROR, "系统出错,请稍后重试~");
                }
            } finally {
                //释放锁
                redisTemplate.delete(TEAM_ADD_LOCK + userId);
            }
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public List<TeamVO> searchTeam(TeamSearchRequest teamSearchRequest, HttpServletRequest request) {

        //1. 根据请求参数添加查询条件
        LambdaQueryWrapper<Team> queryWrapper = isTeamSearchRequest(teamSearchRequest);

        //2. 获取登录的用户
        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        //   4. 只有管理员和队长本人才能查看私密房间
//        Long createUserId = teamSearchRequest.getCreateUserId();
//        User dbCreateUser = userService.getById(createUserId);
//        if (dbCreateUser == null) {
//            throw new BusinessException(StatusEnum.NULL_ERROR, "用户不存在");
//        }
//        if (UserConstant.ADMIN_ROLE != dbCreateUser.getRole() && createUserId != userId) {
//            throw new BusinessException(StatusEnum.NO_AUTH, "无权操作");
//        }
        //3. 关联查询已加入队伍的用户信息
        List<Team> teamList = this.list(queryWrapper);
        if (teamList.isEmpty() || teamList.size() < 1) {
            throw new BusinessException(StatusEnum.NULL_ERROR, "队伍不存在");
        }
        List<TeamVO> teamVOList = new ArrayList<>();
        teamList.stream().forEach(team -> {
            TeamVO teamVO = new TeamVO();
            teamVO.setId(team.getId());
            teamVO.setName(team.getName());
            teamVO.setDescription(team.getDescription());
            teamVO.setStatus(team.getStatus());
            teamVO.setPassword(team.getPassword());
            teamVO.setCreateUserId(team.getCreateUserId());
            teamVO.setMaxNum(team.getMaxNum());
            teamVO.setExpireTime(team.getExpireTime());
            teamVO.setCreateTime(team.getCreateTime());
            teamVO.setUpdateTime(team.getUpdateTime());
            Long teamId = team.getId();
            List<UserTeam> userTeamList = userTeamService.list(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getId, teamId));
            List<Long> userIdList = userTeamList.stream().map(UserTeam::getUserId).collect(Collectors.toList());
            List<User> userList = userService.listByIds(userIdList);
            List<UserVO> userVOList = BeanUtil.copyToList(userList, UserVO.class);
            teamVO.setUserList(userVOList);
            teamVOList.add(teamVO);
        });
        return teamVOList;
    }

    private static void hasTeamSaveRequest(TeamAddRequest teamAddRequest) {
        //3. 校验信息
        //   1. 队名必须存在且长度<20
        String teamName = teamAddRequest.getName();
        if (StringUtils.isBlank(teamName) || teamName.length() > 20) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "队名过长");
        }
        //   2. 队伍类型必须存在
        Integer teamStatus = teamAddRequest.getStatus();
        if (teamStatus == null || teamStatus > 2) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "队伍类型错误");
        }
        //   3. 若队伍类型为2-加密 ,则需要判断密码是否存在 且长度=4
        String password = null;
        if (teamStatus == 2) {
            password = teamAddRequest.getPassword();
            if (StringUtils.isBlank(password) || password.length() != 4) {
                throw new BusinessException(StatusEnum.PARAMS_ERROR, "密码输入错误");
            }
        }
        //   4. 最大人数:  人数 > 1 且 < 20 (默认为5)
        Integer maxNum = teamAddRequest.getMaxNum();
        if (maxNum == null || maxNum < 1 || maxNum > 20) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "队伍人数设置错误");
        }
        //   5. 过期时间 > 当前时间
        Date expireTime = teamAddRequest.getExpireTime();
        if (expireTime == null || expireTime.before(new Date())) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "过期时间错误");
        }
        //   6. 队伍描述 < 512个字符
        String description = teamAddRequest.getDescription();
        if (StringUtils.isBlank(description) || description.length() > 512 || description.length() < 0) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "队伍描述错误");
        }
    }

    private static LambdaQueryWrapper<Team> isTeamSearchRequest(TeamSearchRequest teamSearchRequest) {
        //1. 请求参数是否为空
        if (teamSearchRequest == null) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "请求参数有误");
        }

        //   1. 若请求参数存在,则作为查询条件
        LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();
        Long id = teamSearchRequest.getId();
        if (id != null && id > 0){
            queryWrapper.eq(Team::getId,id);
        }
        //   2. 不展示已过期的队伍
        Date expireTime = teamSearchRequest.getExpireTime();
        queryWrapper.le(expireTime != null, Team::getExpireTime, expireTime);
        //根据创建者查询
        Long createUserId = teamSearchRequest.getCreateUserId();
        queryWrapper.eq(createUserId!=null && createUserId >=0, Team::getCreateUserId, createUserId);
        //   3. 根据某个关键词(name + description)进行搜索
        String teamName = teamSearchRequest.getName();
        String description = teamSearchRequest.getDescription();
        queryWrapper.like(StringUtils.isNotBlank(teamName), Team::getName, teamName)
                .or()
                .like(StringUtils.isNotBlank(description), Team::getDescription, description);
        Integer status = teamSearchRequest.getStatus();
        queryWrapper.eq(status != null && status >= 0, Team::getStatus, status);
        Integer maxNum = Optional.ofNullable(teamSearchRequest.getMaxNum()).orElse(-1);
        queryWrapper.eq(maxNum > 0, Team::getMaxNum, maxNum);
        return queryWrapper;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Page<TeamVO> searchTeamPage(TeamSearchRequest teamSearchRequest, HttpServletRequest request) {
        //1. 根据请求参数添加查询条件
        LambdaQueryWrapper<Team> queryWrapper = isTeamSearchRequest(teamSearchRequest);

        //2. 获取登录的用户
        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        //   4. 只有管理员和队长本人才能查看私密房间
//        Long createUserId = teamSearchRequest.getCreateUserId();
//        User dbCreateUser = userService.getById(createUserId);
//        if (dbCreateUser == null) {
//            throw new BusinessException(StatusEnum.NULL_ERROR, "用户不存在");
//        }
//        if (UserConstant.ADMIN_ROLE != dbCreateUser.getRole() && createUserId != userId) {
//            throw new BusinessException(StatusEnum.NO_AUTH, "无权操作");
//        }
        int current = teamSearchRequest.getCurrent();
        int pageSize = teamSearchRequest.getPageSize();

        //3. 分页关联查询已加入队伍的用户信息
//        List<Team> teamList = this.list(queryWrapper);
        Page<Team> teamPageList = this.page(Page.of(current, pageSize), queryWrapper);
        List<Team> teamList = teamPageList.getRecords();
        if (teamList.isEmpty() || teamList.size() < 1) {
            throw new BusinessException(StatusEnum.NULL_ERROR, "队伍不存在");
        }
        List<TeamVO> teamVOList = new ArrayList<>();
        teamList.stream().forEach(team -> {
            TeamVO teamVO = new TeamVO();
            teamVO.setId(team.getId());
            teamVO.setName(team.getName());
            teamVO.setDescription(team.getDescription());
            teamVO.setStatus(team.getStatus());
            teamVO.setPassword(team.getPassword());
            teamVO.setCreateUserId(team.getCreateUserId());
            teamVO.setMaxNum(team.getMaxNum());
            teamVO.setExpireTime(team.getExpireTime());
            teamVO.setCreateTime(team.getCreateTime());
            teamVO.setUpdateTime(team.getUpdateTime());
            Long teamId = team.getId();
            List<UserTeam> userTeamList = userTeamService.list(new LambdaQueryWrapper<UserTeam>().eq(UserTeam::getTeamId, teamId));
            List<Long> userIdList = userTeamList.stream().map(UserTeam::getUserId).collect(Collectors.toList());
            //用户是否存在于队伍中
            boolean contains = userIdList.contains(userId);
            teamVO.setHasJoin(contains);
            List<User> userList = userService.listByIds(userIdList);
            List<UserVO> userVOList = BeanUtil.copyToList(userList, UserVO.class);



            teamVO.setUserList(userVOList);
            teamVOList.add(teamVO);
        });
        Page<TeamVO> teamVOPageList = new Page<>();
        teamVOPageList.setRecords(teamVOList);
        teamVOPageList.setTotal(teamPageList.getTotal());
        teamVOPageList.setSize(teamPageList.getSize());
        teamVOPageList.setCurrent(teamPageList.getCurrent());

        return teamVOPageList;
    }

    @Override
    public boolean removeTeam(TeamQuitRequest teamQuitRequest, HttpServletRequest request) {

        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        //2. 判断 是否有权限退出队伍
        Team dbTeam = hasAutnQuitTeam(teamQuitRequest, userId);
        //4. 只允许队长及管理员有权限
        if (UserConstant.ADMIN_ROLE != currentUser.getRole() || userId != dbTeam.getCreateUserId()) {
            throw new BusinessException(StatusEnum.NO_AUTH, "暂无权限解散队伍");
        }
        //5. 解散队伍
        //删除队伍内所有的user_team联系
        //查询到 teamId 下的 userTeam 信息
        Long teamId = teamQuitRequest.getTeamId();
        List<UserTeam> userTeamList = userTeamService
                .lambdaQuery()
                .eq(UserTeam::getTeamId, teamId)
                .list();
        //获取 teamId 下的 所有 userId
        List<Long> userIdList = userTeamList.stream().map(UserTeam::getUserId).collect(Collectors.toList());
        //根据 teamId 及 所有的 userId 批量删除 user_team
        boolean removeResult = userTeamService
                .lambdaUpdate()
                .eq(UserTeam::getTeamId, teamId)
                .in(UserTeam::getUserId, userIdList)
                .remove();
        if (!removeResult) {
            throw new BusinessException(StatusEnum.SYSTEM_ERROR, "系统异常,请稍后重试~");
        }
        //删除队伍
        return this.removeById(teamId);
    }

    /**
     * 1.队伍是否存在
     * 2.用户是否加入该队伍
     *
     * @param teamQuitRequest
     * @param userId
     * @return
     */
    private Team hasAutnQuitTeam(TeamQuitRequest teamQuitRequest, Long userId) {

        //1. 判断请求参数是否为空
        if (teamQuitRequest == null) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "请求参数有误");
        }
        Long teamId = teamQuitRequest.getTeamId();

        Team dbTeam = this.getById(teamId);
        if (dbTeam == null || dbTeam.getId() != teamId) {
            throw new BusinessException(StatusEnum.NULL_ERROR, "队伍不存在");
        }
        //3. 用户是否加入该队伍
        boolean exists = userTeamService
                .lambdaQuery()
                .eq(userId != null && userId > 0, UserTeam::getUserId, userId)
                .eq(teamId != null && teamId > 0, UserTeam::getTeamId, teamId)
                .exists();
        if (!exists) {
            throw new BusinessException(StatusEnum.SIMPLE_ERROR, "未加入该队伍");
        }
        return dbTeam;
    }

    @Override
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, HttpServletRequest request) {

        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        //2. 判断是否有权限退出队伍
        Team dbTeam = hasAutnQuitTeam(teamQuitRequest, userId);

        //3. 查询队伍剩余人数
        Long teamId = teamQuitRequest.getTeamId();
        Long count = userTeamService.lambdaQuery().eq(UserTeam::getTeamId, teamId).count();
        if (count == null || count < 1) {
            throw new BusinessException(StatusEnum.NULL_ERROR, "查询不到该队伍");
        }
        //   - 如果队伍只有一人:退出队伍,解散该队伍
        Long createUserId = dbTeam.getCreateUserId();
        if (count == 1) {
            return this.removeTeam(teamQuitRequest, request);
        } else {
            //   - 至少有两人:
            LambdaQueryWrapper<UserTeam> wrapper = new LambdaQueryWrapper<UserTeam>()
                    .eq(UserTeam::getUserId, userId)
                    .eq(UserTeam::getTeamId, teamId);
            //     - 用户退出:退出队伍
            //     - 队长退出:顺位队长给早来的用户,退出队伍
            if (userId == createUserId) {
                //查询到最早加入的两位
                List<UserTeam> userTeamList = userTeamService
                        .lambdaQuery()
                        .eq(UserTeam::getTeamId, teamId)
                        .last("order by id asc limit 2")
                        .list();
                if (userTeamList.size() != 2) {
                    throw new BusinessException(StatusEnum.SYSTEM_ERROR, "系统异常,请稍后重试~");
                }
                Long nextCreateUserId = userTeamList.get(1).getUserId();
                //修改 create_user_id
                dbTeam.setCreateUserId(nextCreateUserId);
                this.updateById(dbTeam);
            }
            //user_team 删除用户信息
            return userTeamService.remove(wrapper);
        }

    }

    @Override
    public boolean updateTeam(TeamAddRequest teamAddRequest, HttpServletRequest request) {

        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        //1. 请求参数是否为空
        if (teamAddRequest == null) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "参数异常");
        }
        //2. 队伍是否存在
        Long teamId = Optional.ofNullable(teamAddRequest.getId()).orElse(-1L);
        if (teamId <= 0) {
            throw new BusinessException(StatusEnum.NULL_ERROR, "队伍不存在");
        }
        //3. 只允许队长及管理员有权限
        Integer status = currentUser.getStatus();
        Team dbTeam = this.getById(teamId);
        Long createUserId = dbTeam.getCreateUserId();
        if (UserConstant.ADMIN_ROLE != status && userId != createUserId) {
            throw new BusinessException(StatusEnum.NO_AUTH, "当前用户无权限修改");
        }
        //4. 若修改status(0-公开,1-私有,2-加密)
        //   - status由 0,1 -> 2:需要添加password
        String description = teamAddRequest.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "密码过长");
        }
        Date expireTime = teamAddRequest.getExpireTime();
        if (expireTime == null || expireTime.before(new Date())) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "最后期限不能低于现在");
        }
        String teamName = teamAddRequest.getName();
        if (StringUtils.isNotBlank(teamName) && teamName.length() >= 20) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "队伍名称过长");
        }
        Integer teamStatus = teamAddRequest.getStatus();
        String password = teamAddRequest.getPassword();
        if (TeamStatusEnum.SECRET == TeamStatusEnum.getEnumByValue(teamStatus)) {
            if (StringUtils.isBlank(password) || password.length() > 20) {
                throw new BusinessException(StatusEnum.PARAMS_ERROR, "请设置密码");
            }
        } else {
            //   - status由 2 -> 0,1:需要删除password
            if (StringUtils.isNotBlank(password)){
                throw new BusinessException(StatusEnum.PARAMS_ERROR, "公开/私密房间禁止设置密码");
            }
        }
        Team updateTeam = new Team();
        BeanUtil.copyProperties(teamAddRequest, updateTeam);
        return this.updateById(updateTeam);
    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, HttpServletRequest request) {
        //1. 用户是否登录
        User currentUser = userService.getCurrentUser(request);
        //2. 请求参数是否为空
        if (teamJoinRequest == null) {
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "参数异常");
        }
        //3. 该队伍是否已经过期
        Long id = teamJoinRequest.getId();
        Team dbTeam = this.getById(id);
        if (dbTeam == null){
            throw new BusinessException(StatusEnum.PARAMS_ERROR, "没有该队伍");
        }
        Date expireTime = dbTeam.getExpireTime();
        if (expireTime == null || expireTime.before(new Date())) {
            throw new BusinessException(StatusEnum.SIMPLE_ERROR, "已超过最后期限");
        }
        //4. 该队伍是否已经满员
        Integer maxNum = dbTeam.getMaxNum();
        List<UserTeam> userTeamList = userTeamService.lambdaQuery().eq(UserTeam::getTeamId, id).list();
        if (userTeamList.size() >= maxNum) {
            throw new BusinessException(StatusEnum.SIMPLE_ERROR, "队伍人员已满");
        }
        //5. 用户不能加入自己的队伍
        Long userId = currentUser.getId();
        if (userId == dbTeam.getCreateUserId()) {
            throw new BusinessException(StatusEnum.SIMPLE_ERROR, "不能加入自己的队伍");
        }
        //6. 用户是否已经存在该队伍
        boolean anyMatch = userTeamList.stream().anyMatch(userTeam -> {
            if (userTeam.getUserId() == userId) {
                return true;
            }
            return false;
        });
        if (anyMatch) {
            throw new BusinessException(StatusEnum.SIMPLE_ERROR, "已经存在于该队伍");
        }
        //7. 用户加入队伍是否上限(上限为10)
        Long count = userTeamService.lambdaQuery().eq(UserTeam::getUserId, userId).count();
        if (count > 10) {
            throw new BusinessException(StatusEnum.SIMPLE_ERROR, "用户加入队伍上限(最多10个)");
        }
        //8. 用户不能加入私密队伍
        TeamStatusEnum teamStatusEnum = dbTeam.getStatus();
        if (teamStatusEnum == TeamStatusEnum.PRIVATE) {
            throw new BusinessException(StatusEnum.NO_AUTH, "不允许加入私密队伍");
        }
        //9. 用户加入加密队伍,密码必须匹配
        if (teamStatusEnum == TeamStatusEnum.SECRET) {
            String requestPassword = teamJoinRequest.getPassword();
            if (StringUtils.isBlank(requestPassword) || requestPassword.length() != 4) {
                throw new BusinessException(StatusEnum.PARAMS_ERROR, "设置密码超过最大长度");
            }
            String teamPassword = dbTeam.getPassword();
            if (teamPassword == null || !teamPassword.equals(requestPassword)) {
                throw new BusinessException(StatusEnum.NO_AUTH, "密码错误");
            }
        }
        //10. user_team新增关联信息
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(id);


        return userTeamService.save(userTeam);
    }

    @Override
    public List<TeamVO> searchTeamMyJoin(HttpServletRequest request) {
        User currentUser = userService.getCurrentUser(request);
        Long userId = currentUser.getId();

        LambdaQueryWrapper<UserTeam> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(userId != null && userId >= 0, UserTeam::getUserId, userId);
        List<UserTeam> userTeamList = userTeamService.list(wrapper);
        List<Long> teamIdList = userTeamList.stream().map(UserTeam::getTeamId).toList();
        LambdaQueryWrapper<Team> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(!teamIdList.isEmpty() && teamIdList.size() > 0, Team::getId, teamIdList);
        List<Team> teamList = this.list(queryWrapper);
        return BeanUtil.copyToList(teamList, TeamVO.class);
    }

}




